/**
 * 项目名：	Rumba Plan
 * 
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2006，所有权利保留。
 * 
 * 文件名：	BCartridgeMgr.java
 * 模块说明：	
 * 修改历史：
 * 2006-10-25 - lxm - 创建。
 */
package com.hd123.rumba.cartridge.biz;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Transaction;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.hd123.rumba.cartridge.CCartridge;
import com.hd123.rumba.cartridge.ICartFunctionView;
import com.hd123.rumba.cartridge.ICartridge;
import com.hd123.rumba.cartridge.ICartridgeMgr;
import com.hd123.rumba.cartridge.entry.Entry;
import com.hd123.rumba.domain.IDomainMgr;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.runtime.IManager;
import com.hd123.rumba.runtime.NamingMgr;
import com.hd123.rumba.runtime.OperateContext;
import com.hd123.rumba.runtime.biz.BManager;
import com.hd123.rumba.user.IUserMgr;
import com.hd123.rumba.util.Version;
import com.hd123.rumba.util.XMLUtil;

/**
 * 构件包 | 业务层 | 管理员 | 实现类
 * 
 * @author lxm
 * 
 */
public class BCartridgeMgr extends BManager implements IBCartridgeMgr {

  private List<BCartridge> cartRegistry = new ArrayList();

  @Override
  public IManager getPerzMgr() throws BusinessException {
    return getCartMgr();
  }

  public void registerCartridges(List<String> cartUuids) throws BusinessException {
    Logger logger = Logger.getLogger(BCartridgeMgr.class);
    cartRegistry.clear();
    for (int i = 0; i < cartUuids.size(); i++) {
      String uuid = cartUuids.get(i);
      logger.info("Cartridge: " + uuid);
      try {
        BCartridge cart = (BCartridge) Class.forName(uuid).newInstance();
        cart.init();
        cartRegistry.add(cart);
      } catch (Exception e) {
        throw new BusinessException("将" + CCartridge.CAPTION_CLASS + "(" + uuid
            + ")加入内存注册表的时候发生错误：" + e.getMessage());
      }
    }
  }

  public void verifyCartridges() throws BusinessException, HibernateException {
    for (int i = 0; i < cartRegistry.size(); i++) {
      BCartridge bcart = cartRegistry.get(i);
      ICartridge pcart = getCartMgr().getCartridge(bcart.getUuid());
      if (pcart == null || pcart.getUpgrading().booleanValue())
        throw new BusinessException("指定的" + CCartridge.CAPTION_CLASS + " " + bcart.getUuid()
            + " 尚未被正确安装到数据库中，请正确安装后重试。");
      if (bcart.getVersion().truncate(3).compareTo(pcart.getVersion().truncate(3)) > 0)
        throw new BusinessException("指定的" + CCartridge.CAPTION_CLASS + " " + bcart.getUuid()
            + " 程序版本(" + bcart.getVersion().toString() + ")与数据结构版本("
            + pcart.getVersion().toString() + ")不匹配，请正确升级后重试。");
    }
  }

  public Version getDBVersion(String cartUuid) throws BusinessException {
    this.checkParameterNotNull(cartUuid, "cartUuid");
    if (getCartridge(cartUuid) == null)
      throw new BusinessException("指定的" + CCartridge.CAPTION_CLASS + "(" + cartUuid
          + ")未注册，请检查配置文件是否正确。");

    return getCartMgr().getDBVersion(cartUuid);
  }

  public void beginUpgradeT(String cartUuid) throws BusinessException, HibernateException {
    this.checkParameterNotNull(cartUuid, "cartUuid");

    getCartMgr().beginUpgradeT(cartUuid);
  }

  public void endUpgradeT(String cartUuid, Version version) throws BusinessException,
      HibernateException {
    this.checkParameterNotNull(cartUuid, "cartUuid");
    this.checkParameterNotNull(version, "version");
    if (getCartridge(cartUuid) == null)
      throw new BusinessException("指定的" + CCartridge.CAPTION_CLASS + "(" + cartUuid
          + ")未注册，请检查配置文件是否正确。");

    getCartMgr().endUpgradeT(cartUuid, version);
  }

  public void saveCartridgeForInstallT(String cartUuid, Version version) throws BusinessException,
      HibernateException {
    this.checkParameterNotNull(cartUuid, "cartUuid");
    this.checkParameterNotNull(version, "version");

    ICartridge pcart = getCartMgr().getCartridge(cartUuid);
    IUserMgr userMgr = (IUserMgr) NamingMgr.getInstance().lookupMgr(IUserMgr.class);
    OperateContext operate = new OperateContext(userMgr.getSysUser());

    Transaction tx = getSession().beginTransaction();
    try {
      BCartridge bcart = this.getCartridge(cartUuid);
      if (pcart == null)
        pcart = getCartMgr().createCartridge();
      this.writeToCartridge(bcart, pcart);
      pcart.setVersion(version);
      getCartMgr().saveCartridge(pcart, operate);
      this.saveFunctionViews(bcart, pcart, operate);
      tx.commit();
    } catch (BusinessException e) {
      tx.rollback();
      throw e;
    } catch (HibernateException e) {
      tx.rollback();
      throw e;
    } finally {
      getSession().flush();
    }
  }

  /** @see IBCartridgeMgr#getAllCartridges() */
  public List<BCartridge> getAllCartridges() throws BusinessException {
    return cartRegistry;
  }

  public BCartridge getCartridge(String uuid) throws BusinessException {
    this.checkParameterNotNull(uuid, "uuid");
    for (int i = 0; i < cartRegistry.size(); i++)
      if (cartRegistry.get(i).getUuid().equals(uuid))
        return cartRegistry.get(i);
    return null;
  }

  public BCartridge getCartridgeByPrefix(String prefix) throws BusinessException {
    for (int i = 0; i < cartRegistry.size(); i++)
      if (cartRegistry.get(i).getPrefix().equals(prefix))
        return cartRegistry.get(i);
    return null;
  }

  public BCartFunctionView getFunctionView(String uuid) throws BusinessException {
    this.checkParameterNotNull(uuid, "uuid");
    for (int i = 0; i < cartRegistry.size(); i++) {
      BCartridge cart = cartRegistry.get(i);
      for (int j = 0; j < cart.getFunctionViews().size(); j++)
        if (cart.getFunctionViews().get(j).getUuid().equals(uuid))
          return cart.getFunctionViews().get(j);
    }
    return null;
  }

  public List<BCartFunctionView> getAllFunctionViews() throws BusinessException {
    List<BCartFunctionView> cfvs = new ArrayList<BCartFunctionView>();
    for (BCartridge cart : cartRegistry) {
      for (BCartFunctionView cfv : cart.getFunctionViews())
        cfvs.add(cfv);
    }
    return cfvs;
  }

  public BCartFunctionView getFunctionViewByPrefix(String prefix) throws BusinessException {
    for (int i = 0; i < cartRegistry.size(); i++) {
      BCartridge cart = cartRegistry.get(i);
      for (int j = 0; j < cart.getFunctionViews().size(); j++)
        if (cart.getFunctionViews().get(j).getPrefix().equals(prefix))
          return cart.getFunctionViews().get(j);
    }
    return null;
  }

  public List<BCartFunctionView> getReferringCFVs(String referredCFVUuid) throws BusinessException {
    BCartFunctionView refCFV = this.getFunctionView(referredCFVUuid);
    List<BCartFunctionView> cfvs = new ArrayList();
    if (refCFV == null)
      return cfvs;
    String refPrefix = refCFV.getPrefix();
    for (int i = 0; i < cartRegistry.size(); i++) {
      BCartridge cart = cartRegistry.get(i);
      for (int j = 0; j < cart.getFunctionViews().size(); j++) {
        BCartFunctionView cfv = cart.getFunctionViews().get(i);
        if (cfv.isReferring2(refPrefix))
          cfvs.add(cfv);
      }
    }
    return cfvs;
  }

  public Entry getEntryById(Class cfvClass, String id) throws BusinessException {
    BCartFunctionView cfv = this.getFunctionView(cfvClass.getName());
    if (cfv == null)
      return null;
    return cfv.getEntries().get(id);
  }

  public <T extends BCartridge> Document getCartridgeConfigDocument(Class<T> cart)
      throws BusinessException {
    assert cart != null;
    String className = cart.getName();
    String fileName = "/" + className.replace(".", "/") + ".cart.xml";
    InputStream is = cart.getResourceAsStream(fileName);
    if (is == null)
      return null;
    return XMLUtil.parse(is);
  }

  public void initCartridgeByConfig(BCartridge cart, Document doc) throws BusinessException {
    assert cart != null;
    assert doc != null;

    List<Element> lsCartridge = XMLUtil.getChildElementsByTagName(doc, "cartridge");
    for (Element eCartridge : lsCartridge) {
      String prefix = XMLUtil.readString(eCartridge, "prefix");
      if (!prefix.equals(cart.getPrefix()))
        throw new BusinessException("配置文件所对应的cartridge.prefix取值不匹配。");

      cart.setCaption(XMLUtil.readString(eCartridge, "caption"));
      cart.setCaptionEN(XMLUtil.readString(eCartridge, "captionEN"));
      cart.setRemark(XMLUtil.readString(eCartridge, "remark"));
      cart.setAsCover(XMLUtil.readBoolean(eCartridge, "asCover"));
      cart.setCopyright(XMLUtil.readString(eCartridge, "copyright"));

      initCartridgeVersion(eCartridge, cart);
      initCartridgeResources(eCartridge, cart);
      initCartridgeReferences(eCartridge, cart);
      initCartridgeUpgrades(eCartridge, cart);
      initCartridgeCFVs(eCartridge, cart);
    }
  }

  public void initCFVByConfig(BCartFunctionView cfv, Element e) throws BusinessException {
    assert cfv != null;
    assert e != null;

    String prefix = XMLUtil.readString(e, "prefix");
    if (!cfv.getPrefix().equals(prefix))
      throw new BusinessException("配置文件所对应的cartrige.cfvs.cfv.prefix取值不匹配。");
    cfv.setCaption(XMLUtil.readString(e, "caption"));
    cfv.setIsAbstract(XMLUtil.readBoolean(e, "isAbstract"));
    cfv.setRemark(XMLUtil.readString(e, "remark"));

    initCFVReferences(e, cfv);
  }

  private ICartridgeMgr cartMgr = null;
  private IDomainMgr domainMgr = null;

  private ICartridgeMgr getCartMgr() throws BusinessException {
    if (cartMgr == null)
      cartMgr = (ICartridgeMgr) NamingMgr.getInstance().lookupMgr(ICartridgeMgr.class);
    return cartMgr;
  }

  private IDomainMgr getDomainMgr() throws BusinessException {
    if (domainMgr == null)
      domainMgr = (IDomainMgr) NamingMgr.getInstance().lookupMgr(IDomainMgr.class);
    return domainMgr;
  }

  private void writeToCartridge(BCartridge cart, ICartridge pcart) throws BusinessException,
      HibernateException {
    pcart.setAssignedUuid(cart.getUuid());
    // Cartridge的域应始终是根域。
    if (pcart.getDomain() == null)
      pcart.setDomain(getDomainMgr().getRootDomain());
    pcart.setPrefix(cart.getPrefix());
    pcart.setCaption(cart.getCaption());
    pcart.setVersion(cart.getVersion());
    pcart.setRemark(cart.getRemark());
  }

  private void saveFunctionViews(BCartridge bcart, ICartridge pcart, OperateContext operate)
      throws BusinessException, HibernateException {
    assert bcart != null;
    assert pcart != null;
    assert operate != null;

    List<ICartFunctionView> pcfvs = getCartMgr().getFunctionViews(pcart);
    for (int i = pcfvs.size() - 1; i >= 0; i--) {
      ICartFunctionView pcfv = pcfvs.get(i);
      boolean exists = false;
      for (BCartFunctionView bcfv : bcart.getFunctionViews())
        if (bcfv.getUuid().equals(pcfv.getUuid())) {
          exists = true;
          break;
        }
      if (!exists)
        getCartMgr().removeFunctionView(pcfv, operate);
    }

    for (BCartFunctionView bcfv : bcart.getFunctionViews()) {
      ICartFunctionView pcfv = null;
      for (ICartFunctionView pcfv2 : pcfvs)
        if (pcfv2.getUuid().equals(bcfv.getUuid())) {
          pcfv = pcfv2;
          break;
        }
      if (pcfv == null)
        pcfv = getCartMgr().createFunctionView(pcart);
      this.writeToFunctionView(pcart, bcfv, pcfv);
      getCartMgr().saveFunctionView(pcfv, operate);
    }
  }

  private void writeToFunctionView(ICartridge pcart, BCartFunctionView cfv, ICartFunctionView pcfv)
      throws BusinessException, HibernateException {
    pcfv.setAssignedUuid(cfv.getUuid());
    // CartFunctionView的域应始终是根域。
    if (pcfv.getDomain() == null)
      pcfv.setDomain(getDomainMgr().getRootDomain());
    pcfv.setPrefix(cfv.getPrefix());
    pcfv.setCaption(cfv.getCaption());
    pcfv.setIsAbstract(cfv.getIsAbstract());
    pcfv.setRemark(cfv.getRemark());
  }

  private void initCartridgeVersion(Element eCartridge, BCartridge cart) throws BusinessException {
    List<Element> list = XMLUtil.getChildElementsByTagName(eCartridge, "version");
    for (Element eVersion : list) {
      cart.setVersion(this.readVersionFromXML(eVersion));
    }
  }

  private Version readVersionFromXML(Element eVersion) {
    assert eVersion != null;
    Integer macroVer = XMLUtil.readInteger(eVersion, "macroVer");
    Integer minorVer = XMLUtil.readInteger(eVersion, "minorVer");
    Integer release = XMLUtil.readInteger(eVersion, "release");
    Integer buildNo = XMLUtil.readInteger(eVersion, "buildNo");
    return new Version(macroVer.intValue(), minorVer.intValue(), release.intValue(), buildNo
        .intValue());
  }
  
  private void initCartridgeResources(Element eCartridge, BCartridge cart) {
    cart.getReferences().clear();
    
    List<Element> lsResources = XMLUtil.getChildElementsByTagName(eCartridge, "resources");
    for (Element eResources: lsResources) {
      
      List<Element> lsResource = XMLUtil.getChildElementsByTagName(eResources, "resource");
      for (Element eResource: lsResource) {
        String name = XMLUtil.readString(eResource, "name");
        String url = XMLUtil.readString(eResource, "url");
        cart.getResources().put(name, url);
      }
    }
  }

  private void initCartridgeReferences(Element eCartridge, BCartridge cart)
      throws BusinessException {
    cart.getReferences().clear();
    HashSet<String> dupChecker = new HashSet<String>();

    List<Element> lsReferences = XMLUtil.getChildElementsByTagName(eCartridge, "references");
    for (Element eReferences : lsReferences) {

      List<Element> lsRefCart = XMLUtil.getChildElementsByTagName(eReferences, "cartridge");
      for (Element eRefCart: lsRefCart) {
        String prefix = XMLUtil.readString(eRefCart, "prefix");
        if (dupChecker.contains(prefix))
          continue;
        dupChecker.add(prefix);

        NodeList nlVersion = eRefCart.getElementsByTagName("version");
        assert nlVersion.getLength() > 0;
        Element eVersion = (Element) nlVersion.item(0);
        Version version = this.readVersionFromXML(eVersion);

        BCartReference cr = new BCartReference();
        cr.setCartridge(cart);
        cr.setRefCartPrefix(prefix);
        cr.setRefCartVersion(version);
        cart.getReferences().add(cr);
      }
    }
  }

  private void initCartridgeUpgrades(Element eCartridge, BCartridge cart) throws BusinessException {
    cart.getUpgradeClassNames().clear();

    List<Element> lsUpgrades = XMLUtil.getChildElementsByTagName(eCartridge, "upgrades");
    for (Element eUpgrades : lsUpgrades) {
      
      List<Element> lsClass = XMLUtil.getChildElementsByTagName(eUpgrades, "class");
      for (Element eClass : lsClass) {
        String className = XMLUtil.readString(eClass, "name");
        if (!cart.getUpgradeClassNames().contains(className))
          cart.getUpgradeClassNames().add(className);
      }
    }
  }

  private void initCartridgeCFVs(Element eCartridge, BCartridge cart) throws BusinessException {
    cart.getFunctionViews().clear();
    HashSet dupChecker = new HashSet();

    List<Element> lsCFVs = XMLUtil.getChildElementsByTagName(eCartridge, "cfvs");
    for (Element eCFVs : lsCFVs) {

      List<Element> lsCFV = XMLUtil.getChildElementsByTagName(eCFVs, "cfv");
      for (Element eCFV : lsCFV) {

        String className = XMLUtil.readString(eCFV, "name");
        if (dupChecker.contains(className))
          continue;
        dupChecker.add(className);

        String prefix = XMLUtil.readString(eCFV, "prefix");
        if (dupChecker.contains(prefix))
          continue;
        dupChecker.add(prefix);

        BCartFunctionView cfv;
        try {
          cfv = (BCartFunctionView) Class.forName(className).newInstance();
          cfv.setCartridge(cart.cloneSimple());
        } catch (Exception e) {
          throw new BusinessException(e);
        }
        cfv.init(eCFV);
        cart.getFunctionViews().add(cfv);
      }
    }
  }

  private void initCFVReferences(Element e, BCartFunctionView cfv) throws BusinessException {
    cfv.getReferences().clear();
    HashSet dupChecker = new HashSet();

    List<Element> lsReferences = XMLUtil.getChildElementsByTagName(e, "references");
    for (Element eReferences : lsReferences) {

      List<Element> lsCFV = XMLUtil.getChildElementsByTagName(eReferences, "cfv");
      for (Element eCFV : lsCFV) {

        String prefix = XMLUtil.readString(eCFV, "prefix");
        if (dupChecker.contains(prefix))
          continue;
        dupChecker.add(prefix);

        BCFVReference cfvr = new BCFVReference();
        cfvr.setFunctionView(cfv);
        cfvr.setRefCFVPrefix(prefix);
        cfv.getReferences().add(cfvr);
      }
    }
  }

}
